//
//  SeniorRACViewController.m
//  FRP_OC
//
//  Created by DeLong Yang on 2017/8/15.
//  Copyright © 2017年 DeLongYang. All rights reserved.
//

/*
 简书地址：http://www.jianshu.com/p/e10e5ca413b7
 主要用于实验。reactiveCocoa 的一些senior 用法 
 */


#import "SeniorRACViewController.h"
#import <ReactiveObjC/ReactiveObjC.h>
#import <ReactiveObjC/RACReturnSignal.h>

@interface SeniorRACViewController ()

@property (weak, nonatomic) IBOutlet UITextField *racBindTextField;

@property (weak, nonatomic) IBOutlet UITextField *racStreamTextField;

/**
 first subjecet
 */
@property (strong,nonatomic)RACSubject *subject;

/**
 secondSubjecet
 */
@property (strong,nonatomic)RACSubject *secondSubject;

@property (strong,nonatomic)RACSignal *signal;


@end

@implementation SeniorRACViewController

- (void)viewDidLoad {
    [super viewDidLoad];
        //
//    [self testBind];
    
//    [self testFlattenMap];
//    [self testMap];
    
//    [self testOriginalSignals];
//    [self racOperationConcate];
//    [self racThenOperation];
    //
//    [self racMergeOperation];
//    [self racMergeOperationTwo];
//    [self racZipWithOperation];
//    [self racReduceTest];
    
//    [self filterAndIgnoreAndChange];//  过滤一些信号
//    [self takenSkipSwitch];
    //
    [self doNextDoCompleted];
    
//    [self racTimeOperation];  // 超时操作
    
//    [self delaySignal];
    
//    [self retryAndReplay];
//    [self replayMethod];
    //
//    [self  intervalSignal];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dealloc
{
    NSLog(@"SeniorViewController dealloc  ---");
}


#pragma mark ----   Bind use  RACStreamBindBlock
// 方式一:在返回结果后，拼接。

// 方式二:在返回结果前，拼接，使用RAC中bind方法做处理。
// bind方法参数:需要传入一个返回值是RACStreamBindBlock的block参数
// RACStreamBindBlock是一个block的类型，返回值是信号，参数（value,stop），因此参数的block返回值也是一个block。

// RACStreamBindBlock:
// 参数一(value):表示接收到信号的原始值，还没做处理
// 参数二(*stop):用来控制绑定Block，如果*stop = yes,那么就会结束绑定。
// 返回值：信号，做好处理，在通过这个信号返回出去，一般使用RACReturnSignal,需要手动导入头文件RACReturnSignal.h。

// bind方法使用步骤:
// 1.传入一个返回值RACStreamBindBlock的block。
// 2.描述一个RACStreamBindBlock类型的bindBlock作为block的返回值。
// 3.描述一个返回结果的信号，作为bindBlock的返回值。
// 注意：在bindBlock中做信号结果的处理。

// 底层实现:
// 1.源信号调用bind,会重新创建一个绑定信号。
// 2.当绑定信号被订阅，就会调用绑定信号中的didSubscribe，生成一个bindingBlock。
// 3.当源信号有内容发出，就会把内容传递到bindingBlock处理，调用bindingBlock(value,stop)
// 4.调用bindingBlock(value,stop)，会返回一个内容处理完成的信号（RACReturnSignal）。
// 5.订阅RACReturnSignal，就会拿到绑定信号的订阅者，把处理完成的信号内容发送出来。

// 注意:不同订阅者，保存不同的nextBlock，看源码的时候，一定要看清楚订阅者是哪个。
// 这里需要手动导入#import <ReactiveCocoa/RACReturnSignal.h>，才能使用RACReturnSignal。

// 测试 绑定信号的流程
- (void)testBind
{
    //
    [self.racBindTextField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"------- out put :%@",x);
    }];
    
    
    #pragma mark ---- 博客上此处代码有错！！！ 估计是 从别处粘贴过来的！！  笔者做了改进
    
//    [[self.racStreamTextField.rac_textSignal bind:^RACSignalBindBlock{
//        return ^RACStream *(id value,BOOL *stop){
//            return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
//        };
//    }] subscribeNext:^(id  _Nullable x) {
//        
//    }];
    
    // 二者的效果是一样的 那么这种在
    [[self.racStreamTextField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{
        return ^RACSignal *(id value,BOOL *stop){
            return [RACReturnSignal return:[NSString stringWithFormat:@"output %@",value]];
        };
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"bindBlock %@",x);
    }];
    
    // 实验证明 多次
    [[self.racStreamTextField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{
        return ^RACSignal *(id value,BOOL *stop){
            
            NSLog(@"value is: %@",value);
            NSString *str = [NSString stringWithFormat:@"%@",value];
            if ([str isEqualToString:@"hhh"]) {
                *stop = YES;  //  笔者发现如果 设置了stop 为YES 后后面的信号就没有
                return [RACReturnSignal return:@"this is not take into consideration"];
            }
            return [RACReturnSignal return:[NSString stringWithFormat:@"output %@",value]];
        };
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"*****。 bindBlock %@",x);
    }];
}


#pragma mark ---- map and fiattenMap
// 监听文本框的内容改变，把结构重新映射成一个新值.

// flattenMap作用:把源信号的内容映射成一个新的信号，信号可以是任意类型。  注意是信号

// flattenMap使用步骤:
// 1.传入一个block，block类型是返回值RACStream，参数value
// 2.参数value就是源信号的内容，拿到源信号的内容做处理
// 3.包装成RACReturnSignal信号，返回出去。

// flattenMap底层实现:
// 0.flattenMap内部调用bind方法实现的,flattenMap中block的返回值，会作为bind中bindBlock的返回值。
// 1.当订阅绑定信号，就会生成bindBlock。
// 2.当源信号发送内容，就会调用bindBlock(value, *stop)
// 3.调用bindBlock，内部就会调用flattenMap的block，flattenMap的block作用：就是把处理好的数据包装成信号。
// 4.返回的信号最终会作为bindBlock中的返回信号，当做bindBlock的返回信号。
// 5.订阅bindBlock的返回信号，就会拿到绑定信号的订阅者，把处理完成的信号内容发送出来。


// 测试 map 转换 信号
- (void)testFlattenMap
{
    //  在主控制器中 我们已经转换过很多这个信号了
    [[_racBindTextField.rac_textSignal flattenMap:^RACSignal *(id value) {
        
        // block什么时候 : 源信号发出的时候，就会调用这个block。
        
        // block作用 : 改变源信号的内容。
        
        // 返回值：绑定信号的内容. 我们通过block 将信号返回了
        return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
        
    }] subscribeNext:^(id x) {
        
        // 订阅绑定信号，每当源信号发送内容，做完处理，就会调用这个block。
        
        NSLog(@"%@",x);
        
    }];

}

// 监听文本框的内容改变，把结构重新映射成一个新值.

// Map作用:把源信号的值映射成一个新的值

// Map使用步骤:
// 1.传入一个block,类型是返回对象，参数是value
// 2.value就是源信号的内容，直接拿到源信号的内容做处理
// 3.把处理好的内容，直接返回就好了，不用包装成信号，返回的值，就是映射的值。

// Map底层实现:
// 0.Map底层其实是调用flatternMap,Map中block中的返回的值会作为flatternMap中block中的值。
// 1.当订阅绑定信号，就会生成bindBlock。
// 3.当源信号发送内容，就会调用bindBlock(value, *stop)
// 4.调用bindBlock，内部就会调用flattenMap的block
// 5.flattenMap的block内部会调用Map中的block，把Map中的block返回的内容包装成返回的信号。
// 5.返回的信号最终会作为bindBlock中的返回信号，当做bindBlock的返回信号。
// 6.订阅bindBlock的返回信号，就会拿到绑定信号的订阅者，把处理完成的信号内容发送出来。

- (void)testMap
{
    
    [[_racBindTextField.rac_textSignal map:^id(id value) {
        // 当源信号发出，就会调用这个block，修改源信号的内容
        // 返回值：就是处理完源信号的内容。
        return [NSString stringWithFormat:@"输出:%@",value];
    }] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
}

//FlatternMap和Map的区别
//
//1.FlatternMap中的Block返回信号。
//2.Map中的Block返回对象。
//3.开发中，如果信号发出的值不是信号，映射一般使用Map
//4.开发中，如果信号发出的值是信号，映射一般使用FlatternMap。


//signalOfsignals用FlatternMap。    也就是信号中的信号
- (void)testOriginalSignals
{
    //  create signal within signal
    RACSubject *signalOfsignals = [RACSubject subject];
    RACSubject *signal = [RACSubject subject];
    
    [[signalOfsignals flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        
         // 当signalOfsignals的signals发出信号才会调用
        NSLog(@"value class is:%@  value is:%@",[value class],value);
        
        return value;
    }] subscribeNext:^(id  _Nullable x) {
        
        // 只有signalOfsignals的signal发出信号才会调用，因为内部订阅了bindBlock中返回的信号，也就是flattenMap返回的信号。
        // 也就是flattenMap返回的信号发出内容，才会调用。  那么这里的。value 就是。signal 我们没有转换
        
//        NSLog(@"%@aaa",x);
        NSLog(@"+++++:%@",x);  //
    }];
    
    [signalOfsignals sendNext:signal];  // 这是一个很有意思的事情。
    
    // signal 是信号中的信号
    [signal sendNext:@"信号中的信号发送消息"];
    [signal sendNext:@"windows send another signal"];  //
}


#pragma mark ----   Reactive Cocoa Operation Combine

// Rac  信号的组合操作
- (void)racOperationConcate
{
    //
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@1];
        [subscriber sendCompleted];  // testing if we do not sendComplete signalB will not execute  amzaing !
        return  nil;
    }];
    
    //
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        [subscriber sendNext:@2];
//        [subscriber sendCompleted];  在测试三个信号连接的过程中 这句需要 sendCompleted
        // 不然不会有 第三个信号的消息不会订阅成功
        return nil;
    }];
    
     // 把signalA拼接到signalB后，signalA发送完成，signalB才会被激活。
    
    // 以后只需要面对拼接信号开发。
    // 订阅拼接的信号，不需要单独订阅signalA，signalB
    // 内部会自动订阅。
    // 注意：第一个信号必须发送完成，第二个信号才会被激活
    RACSignal  *contactSignal = [signalA concat:signalB];
    [contactSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
       
    }];
    
    
    
    // concat底层实现:
    // 1.当拼接信号被订阅，就会调用拼接信号的didSubscribe
    // 2.didSubscribe中，会先订阅第一个源信号（signalA）
    // 3.会执行第一个源信号（signalA）的didSubscribe
    // 4.第一个源信号（signalA）didSubscribe中发送值，就会调用第一个源信号（signalA）订阅者的nextBlock,通过拼接信号的订阅者把值发送出来.
    // 5.第一个源信号（signalA）didSubscribe中发送完成，就会调用第一个源信号（signalA）订阅者的completedBlock,订阅第二个源信号（signalB）这时候才激活（signalB）。
    // 6.订阅第二个源信号（signalB）,执行第二个源信号（signalB）的didSubscribe
    // 7.第二个源信号（signalA）didSubscribe中发送值,就会通过拼接信号的订阅者把值发送出来.
    
    #pragma mark ---  笔者延伸   我们conact 三个 连续的信号  !!! 待完成
    RACSignal *signalC = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //
        [subscriber sendNext:@"signalC"];
        return nil;
    }];
    
    
    RACSignal *threeContactSignal = [contactSignal concat:signalC];
    [threeContactSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"this is three contacted signal %@",x);
    }];
}

// then operation
- (void)racThenOperation
{
    // 这个是函数式编程的典范
    // then:用于连接两个信号，当第一个信号完成，才会连接then返回的信号
    // 注意使用then，之前信号的值会被忽略掉.
    // 底层实现：1、先过滤掉之前的信号发出的值。2.使用concat连接then返回的信号
    [[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        //
        [subscriber sendNext:@"first signal"];
        [subscriber sendCompleted];
        
        return nil;
    }] then:^RACSignal * _Nonnull{
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            [subscriber sendNext:@"this is then signal"];
            return nil;
        }];
    }] subscribeNext:^(id  _Nullable x) {
       //
        NSLog(@"%@",x);  // 没有打印 then 之前的信号
    }];
}


//  merge operation
- (void)racMergeOperation
{
    //
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"merge signal A"];
        return nil;
    }];
    
    //
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //
        [subscriber sendNext:@"merge signal B"];
        return nil;
    }];
    
    //
    RACSignal *mergeSignal = [signalA merge:signalB];
    [mergeSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //
    // 底层实现：
    // 1.合并信号被订阅的时候，就会遍历所有信号，并且发出这些信号。
    // 2.每发出一个信号，这个信号就会被订阅
    // 3.也就是合并信号一被订阅，就会订阅里面所有的信号。
    // 4.只要有一个信号被发出就会被监听。
}

    #pragma mark ----  笔者延伸的 使用 racSubject 来测试  merge的信号的改变
- (void)racMergeOperationTwo
{
    //
    self.subject = [RACSubject subject];
    self.secondSubject = [RACSubject subject];
    
    RACSignal *mergeSubject = [self.subject merge:self.secondSubject];
    [mergeSubject subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
    }];
    
//    [self.subject sendNext:@"subject one"];
//    [self.secondSubject sendNext:@"subject two"];  // 实际是可以实现的
}


- (IBAction)mergeSubChangeClick:(id)sender
{
    int random = arc4random()%2;
    NSLog(@"%d",random);
    
    // 我们测试 不论那个信号发生改变 这个 merge 的信号也发生 了改变
    if (random == 0) {
        [self.subject sendNext:@"subject signal"];
    }else{
        [self.secondSubject sendNext:@"second subject signal "];
    }
    
}

#pragma  mark ----  racZipWith Operation
//  racZipWith Operation
/*
  zipWith:把两个信号压缩成一个信号，只有当两个信号同时发出信号内容时，并且把两个信号的内容合并成一个元组，才会触发压缩流的next事件。
 */
- (void)racZipWithOperation
{
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        [subscriber sendNext:@"signalA"];
        return nil;
    }];
    
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"signalB"];
        return  nil;
    }];
    
    // 压缩信号A，信号B
    RACSignal *zipSignal = [signalA zipWith:signalB];
    [zipSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
        RACTuple *tuple = x;
        NSArray *arr = tuple.allObjects;
        NSLog(@"first ele is %@",arr[0]);
    }];
    
    // 底层实现:
    // 1.定义压缩信号，内部就会自动订阅signalA，signalB
    // 2.每当signalA或者signalB发出信号，就会判断signalA，signalB有没有发出个信号，有就会把最近发出的信号都包装成元组发出。
    
    // 把两个信号组合成一个信号,跟zip一样，没什么区别  combine 操作
//    RACSignal *combineSignal = [signalA combineLatestWith:signalB];
//    [combineSignal subscribeNext:^(id  _Nullable x) {
//        NSLog(@"%@",x);
//        RACTuple *tuple = x;
//        NSArray *arr = tuple.allObjects;
//        NSLog(@"combine is %@",arr[0]);
//    }];
    
}

#pragma mark ---- rac reduce 信号聚合
// reduce聚合:用于信号发出的内容是元组，把信号发出元组的值聚合成一个值
- (void)racReduceTest
{
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@1];
        
        return nil;
    }];
    
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@2];
        
        return nil;
    }];
    
    // 聚合
    // 常见的用法，（先组合在聚合）。combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock
    // reduce中的block简介:
    // reduceblcok中的参数，有多少信号组合，reduceblcok就有多少参数，每个参数就是之前信号发出的内容
    // reduceblcok的返回值：聚合信号之后的内容。
    RACSignal *reduceSignal = [RACSignal combineLatest:@[signalA,signalB] reduce:^id(NSNumber *num1 ,NSNumber *num2){
        
        return [NSString stringWithFormat:@"%@ %@",num1,num2];
        
    }];
    
    [reduceSignal subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
    // 底层实现:
    // 1.订阅聚合信号，每次有内容发出，就会执行reduceblcok，把信号内容转换成reduceblcok返回的值。
}

#pragma mark ---- filter  and ignore signal
- (void)filterAndIgnoreAndChange
{
    // 过滤:
//    // 每次信号发出，会先执行过滤条件判断.
//    RACSignal *signal = [self.racStreamTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//        return  value.length >3;
//    }];
//
//    [signal subscribeNext:^(id  _Nullable x) {
//        NSLog(@"---- filter %@ ",x);
//    }];
    
//    // 内部调用filter过滤，忽略掉ignore的值 ！！！！！
//    [[self.racStreamTextField.rac_textSignal ignore:@"1"] subscribeNext:^(id x) {
//
//        NSLog(@"%@",x);
//    }];  // 并不是忽略 textField 中的出现的1  而是 整个的text = @"1" 的情况
    
    
//
//    // 对比 没有用
//    RACSignal *ignoreSignal = [self.racStreamTextField.rac_textSignal ignore:@"1"];
//    [ignoreSignal subscribeNext:^(id  _Nullable x) {
//        NSLog(@"ignore signal %@",x);
//    }];
    
    // distinctUntilChanged:当上一次的值和当前的值有明显的变化就会发出信号，否则会被忽略掉。
    // 过滤，当上一次和当前的值不一样，就会发出内容。
    // 在开发中，刷新UI经常使用，只有两次数据不一样才需要刷新 
    [[self.racStreamTextField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
        NSLog(@"distinct until changed is: %@",x);
    }];
    
}

#pragma mark ----  Taken Skip Switch
// take:从开始一共取N次的信号
- (void)takenSkipSwitch
{
    RACSubject *signal = [RACSubject subject];
    [[signal take:3] subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
    }];  // 改变take 的数目测试 0-3
    // 3.发送信号
    [signal sendNext:@1];
    [signal sendNext:@2];  // 如果take 1 说明只会取 1次型号 那个 @2 不会被读取
    [signal sendNext:@"jjj"];
//    [signal sendNext:@"this 4 signal"]; // 我们实验这个信号是否会被打印 实际没有
    
    // takeLast:取最后N次的信号,前提条件，订阅者必须调用完成，因为只有完成，就知道总共有多少信号.
    RACSubject *takeLatestSignal = [RACSubject subject];
    [[takeLatestSignal takeLast:1] subscribeNext:^(id  _Nullable x) {
        NSLog(@"take latest signal %@",x);
    }];
    [takeLatestSignal sendNext:@4];
    [takeLatestSignal sendNext:@5];
    [takeLatestSignal sendCompleted];
    
    //takeUntil:(RACSignal *):获取信号直到某个信号执行完成
    [_racStreamTextField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
    
    // skip:(NSUInteger):跳过几个信号,不接受。 确实有效果
    [[_racStreamTextField.rac_textSignal skip:3] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"skip 1 signal: %@",x);
    }];
    
    //
    RACSubject *signalOfSignals = [RACSubject subject];
    RACSubject *signal2 = [RACSubject subject];
    
    [signalOfSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"switchToLatestSignal: %@",x);
    }];
    
    [signalOfSignals sendNext:signal2];
    [signal2 sendNext:@"hello de long"];
    
}

#pragma mark ----  deliverOn subscribeOn doNext doCompleted timeOut

/**
 doNext: 执行Next之前，会先执行这个Block
 doCompleted: 执行sendCompleted之前，会先执行这个Block
 */
- (void)doNextDoCompleted
{
    [[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"dddd"];
        [subscriber sendCompleted];
        return  nil;
    }] doNext:^(id  _Nullable x) {
        // 执行[subscriber sendNext:@1];之前会调用这个Block  x 是dddd 并不是信号本身
        NSLog(@"doNext x:%@",x);
        
    }] doCompleted:^{
        // 执行[subscriber sendCompleted];之前会调用这个Block
        NSLog(@"doCompleted");;
    }] subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
    }];
    
    //
}


#pragma mark ----  !!!  deliverOn 待测试
/**
 deliverOn: 内容传递切换到制定线程中，副作用在原来线程中,把在创建信号时block中的代码称之为副作用。
 subscribeOn: 内容传递和副作用都会切换到制定线程中。
 */
- (void)deliverOnAndSubscribeOn
{
    
}

#pragma mark ---- RAC 时间操作
- (void)racTimeOperation
{
    // timeout：超时，可以让一个信号在一定的时间后，自动报错。 而在error 的回调里我们可以做些操作
    //
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        return nil;
    }] timeout:1 onScheduler:[RACScheduler currentScheduler]];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    } error:^(NSError *error) {
        // 1秒后会自动调用
        NSLog(@"%@",error);
    }];
    
}

- (void)intervalSignal
{
    // 即使 viewController  dealloc 了 这个timer 没法停止
    [[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"date is %@",x);
    }];
}

/**
 delay 延迟发送next。
 */
- (void)delaySignal
{
    RACSignal *signal = [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@1];
        return nil;
    }] delay:4] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
}

#pragma mark ---- RAC 方法重复
- (void)retryAndReplay
{
    __block int i = 0;
    // retry重试 ：只要失败，就会重新执行创建信号中的block,直到成功.
    // 会陷入一个死循环 如果失败
    [[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        //
        if (i==0) {
            [subscriber sendNext:@"one"];
        }else{
            NSLog(@"接收到错误");
            [subscriber sendError:nil];
        }
        i++;
        return nil;
    }] retry] subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
    } error:^(NSError * _Nullable error) {
        
    }];
}

- (void)replayMethod
{
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        
        return nil;
    }] replay];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"第一个订阅者%@",x);
        
    }];
    
    [signal subscribeNext:^(id x) {
        
        NSLog(@"第二个订阅者%@",x);
        
    }];
    
}


/**
 throttle节流:当某个信号发送比较频繁时，可以使用节流，在某一段时间不发送信号内容，过了一段时间获取信号的最新内容发出。
 */
- (void)throttleTest
{
    RACSubject *signal = [RACSubject subject];
    
//    _signal = signal;
    
    // 节流，在一定时间（1秒）内，不接收任何信号内容，过了这个时间（1秒）获取最后发送的信号内容发出。
    [[signal throttle:1] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
}

// 
- (IBAction)sendMsgConstanet:(id)sender {
}












@end









































































































































